<template>
  <div class="fs-virtual-waterfall-container" ref="containerRef" @scroll="handleScroll">
    <div class="fs-virtual-waterfall-list" :style="listStyle">
      =={{renderList}}=={{temporaryList}}
      <template v-if="isShow">
        <div v-for="item in renderList" :key="item.item.id" class="fs-virtual-waterfall-item" :style="item.style">
          =={{ item }}
          <slot name="item" :item="item.item" :imageHeight="item.imageHeight"></slot>
        </div>
      </template>
      <div id="temporary-list" v-else>
        <div v-for="item in temporaryList" :key="item.item.id" :style="item.style">
          <slot name="item" :item="item.item" :imageHeight="item.imageHeight"></slot>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { CSSProperties, computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from "vue";
import type { IVirtualWaterFallProps, ICardItem, IBookColumnQueue, IBookRenderItem, IBookItemRect } from "./tpye1";
import { debounce, rafThrottle } from "./tool";
console.log(222222);

const props = withDefaults(defineProps<IVirtualWaterFallProps>(), {
  enterSize: 10,
  gap: 8,
  column: 2,
  pageSize: 10
})

defineSlots<{
  item(props: { item: ICardItem; imageHeight: number }): any;
}>();

const containerRef = ref<HTMLDivElement | null>(null);

const resizeObserver = new ResizeObserver(() => {
  handleResize();
});

// 修改数据状态接口
interface IDataState {
  currentPage: number
  loading: boolean
  isFinish: boolean
  list: ICardItem[]
}

// 初始化数据状态
const dataState = reactive<IDataState>({
  currentPage: 1,
  loading: false,
  isFinish: false,
  list: []
})

// 重置方法
const reset = async () => {
  console.log('重置瀑布流')
  dataState.list = []
  dataState.currentPage = 1
  dataState.loading = false
  dataState.isFinish = false
  await loadDataList()
}

// 初始加载数据
onMounted(async () => {
  console.log('瀑布流组件挂载完成，开始加载数据')
  if (containerRef.value) {
    resizeObserver.observe(containerRef.value)
    initScrollState()
  }
  await loadDataList()
})

const scrollState = reactive({
  viewWidth: 0,
  viewHeight: 0,
  start: 0,
});

// 修改队列状态接口
interface IQueueState {
  len: number
  queue: {
    list: IBookRenderItem[]
  }[]
}

// 初始化队列状态
const queueState = reactive<IQueueState>({
  len: 0,
  queue: []
})

const hasMoreData = computed(() => queueState.len < dataState.list.length);

const temporaryList = ref<IBookRenderItem[]>([]);

const isShow = ref(false);

const itemSizeInfo = ref(new Map<ICardItem["id"], IBookItemRect>());

const end = computed(() => scrollState.viewHeight + scrollState.start);

const cardList = computed(() => queueState.queue.reduce<IBookRenderItem[]>((pre, { list }) => pre.concat(list), []));

const renderList = computed(() => {
  console.log('计算renderList:', {
    queue: queueState.queue,
    start: scrollState.start,
    end: end.value
  })

  const list = queueState.queue.reduce<IBookRenderItem[]>((pre, { list }) => pre.concat(list), [])
  console.log('渲染列表:', list)
  return list
})

const computedHeight = computed(() => {
  let minIndex = 0,
    minHeight = Infinity,
    maxHeight = -Infinity;
  queueState.queue.forEach(({ height }, index) => {
    if (height < minHeight) {
      minHeight = height;
      minIndex = index;
    }
    if (height > maxHeight) {
      maxHeight = height;
    }
  });
  return {
    minIndex,
    minHeight,
    maxHeight,
  };
});

const listStyle = computed(() => ({
  height: `${computedHeight.value.minHeight}px`,
  position: 'relative'
}))

watch(
  () => props.column,
  () => {
    handleResize();
  }
);

const setItemSize = () => {
  if (!scrollState.viewWidth) {
    initScrollState();
  }

  console.log('设置item尺寸, viewWidth:', scrollState.viewWidth);

  itemSizeInfo.value = dataState.list.reduce<Map<ICardItem["id"], IBookItemRect>>((pre, current) => {
    const itemWidth = Math.floor((scrollState.viewWidth - (props.column - 1) * props.gap) / props.column);
    const rect = itemSizeInfo.value.get(current.id);
    pre.set(current.id, {
      width: itemWidth,
      height: rect ? rect.height : 0,
      imageHeight: Math.floor((itemWidth * current.height) / current.width),
    });
    return pre;
  }, new Map());

  console.log('itemSizeInfo:', itemSizeInfo.value);
};

const updateItemSize = () => {
  temporaryList.value.forEach(({ item, h }) => {
    const rect = itemSizeInfo.value.get(item.id)!;
    itemSizeInfo.value.set(item.id, { ...rect, height: h });
  });
};

const addInQueue = (size: number) => {
  console.log('添加到队列:', { size, temporaryList: temporaryList.value })

  const columnWidth = Math.floor((scrollState.viewWidth - (props.column - 1) * props.gap) / props.column)
  const columnHeights = new Array(props.column).fill(0)
  const newItems: IBookRenderItem[] = []

  temporaryList.value.forEach((item) => {
    // 找到最短的列
    const minHeight = Math.min(...columnHeights)
    const columnIndex = columnHeights.indexOf(minHeight)

    // 计算位置
    const x = columnIndex * (columnWidth + props.gap)
    const y = minHeight + (minHeight > 0 ? props.gap : 0)

    // 更新列高度
    columnHeights[columnIndex] = y + item.h

    // 添加新项
    newItems.push({
      ...item,
      x,
      y,
      style: {
        ...item.style,
        position: 'absolute',
        left: `${x}px`,
        top: `${y}px`
      }
    })
  })

  console.log('新增项目:', newItems)

  // 更新队列
  queueState.queue.push({ list: newItems })
  queueState.len += size

  // 更新容器高度
  computedHeight.value = {
    minHeight: Math.max(...columnHeights)
  }
};

const generatorItem = (item: ICardItem, before: IBookRenderItem | null, index: number): IBookRenderItem => {
  const rect = itemSizeInfo.value.get(item.id)!;
  const width = rect.width;
  const height = rect.height;
  const imageHeight = rect.imageHeight;
  let y = 0;
  if (before) y = before.y + before.h + props.gap;
  return {
    item,
    y,
    h: height,
    imageHeight,
    style: {
      width: `${width}px`,
      height: `${height}px`,
      transform: `translate3d(${index === 0 ? 0 : (width + props.gap) * index}px, ${y}px, 0)`,
    },
  };
};

const loadDataList = async () => {
  console.log('加载数据:', {
    page: dataState.currentPage,
    loading: dataState.loading,
    finished: dataState.isFinish
  })

  if (dataState.loading || dataState.isFinish) {
    return;
  }

  dataState.loading = true;
  try {
    const list = await props.request(dataState.currentPage, props.pageSize);
    console.log('获取到数据:', list);

    if (!list?.length) {
      dataState.isFinish = true;
      return;
    }

    dataState.list.push(...list);
    console.log('当前累计数据:', dataState.list);
    dataState.currentPage++;

    await nextTick()
    // 设置新数据的尺寸信息
    setItemSize();
    // 挂载新数据到瀑布流
    mountTemporaryList();
  } catch (error) {
    console.error('加载数据失败:', error);
  } finally {
    dataState.loading = false;
  }
};

const handleScroll = rafThrottle(() => {
  const { scrollTop, clientHeight } = containerRef.value!;
  scrollState.start = scrollTop;

  // 添加更严格的条件检查
  if (dataState.loading || dataState.isFinish || !hasMoreData.value) {
    return;
  }

  // 只有在接近底部时才加载
  const scrollBottom = scrollTop + clientHeight;
  const threshold = 200; // 提前200px触发加载

  if (scrollBottom + threshold > computedHeight.value.minHeight) {
    loadDataList();
  }
});

const handleResize = debounce(() => {
  initScrollState();
  reComputedQueue();
}, 300);

const reComputedQueue = () => {
  setItemSize();
  queueState.queue = new Array(props.column).fill(0).map<IBookColumnQueue>(() => ({ list: [], height: 0 }));
  queueState.len = 0;
  containerRef.value!.scrollTop = 0;
  mountTemporaryList(props.pageSize);
};

const mountTemporaryList = (size = props.enterSize) => {
  console.log('开始挂载临时列表, size:', size);
  if (!hasMoreData.value) return;

  isShow.value = false;
  temporaryList.value = [];

  for (let i = 0; i < size!; i++) {
    const item = dataState.list[queueState.len + i];
    if (!item) break;
    const rect = itemSizeInfo.value.get(item.id)!;
    temporaryList.value.push({
      item,
      y: 0,
      h: 0,
      imageHeight: rect.imageHeight,
      style: {
        width: `${rect.width}px`,
      },
    });
  }

  console.log('临时列表数据:', temporaryList.value);

  nextTick(() => {
    const list = document.querySelector("#temporary-list");
    if (!list) {
      console.error('找不到临时列表元素');
      return;
    }

    [...list.children].forEach((item, index) => {
      const rect = item.getBoundingClientRect();
      temporaryList.value[index].h = rect.height;
    });

    console.log('更新后的临时列表:', temporaryList.value);
    isShow.value = true;
    updateItemSize();
    addInQueue(temporaryList.value.length);
    temporaryList.value = [];
  });
};

const initScrollState = () => {
  scrollState.viewWidth = containerRef.value!.clientWidth;
  scrollState.viewHeight = containerRef.value!.clientHeight;
  scrollState.start = containerRef.value!.scrollTop;
};

const init = async () => {
  initScrollState();
  resizeObserver.observe(containerRef.value!);
  const len = await loadDataList();
  setItemSize();
  len && mountTemporaryList(len);
};

onMounted(() => {
  init();
});

onUnmounted(() => {
  resizeObserver.unobserve(containerRef.value!);
});

// 暴露重置方法
defineExpose({
  reset
})
</script>

<style scoped lang="scss">
.fs-virtual-waterfall {
  &-container {
    width: 100%;
    height: 100%;
    overflow-y: scroll;
    overflow-x: hidden;
  }

  &-list {
    position: relative;
    width: 100%;
  }

  &-item {
    position: absolute;
    top: 0;
    left: 0;
    box-sizing: border-box;
  }
}
</style>
